package online.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * ルート制御フィルタ
 *
 * @author Tadashi Nakayama
 * @version 1.0.0
 */
public abstract class RouteRequestFilter implements Filter {

	/** サーブレットコンテキスト */
	private ServletContext sc = null;
	/** クエリ引き継ぎフラグ */
	private boolean inherit = false;

	/**
	 * @see javax.servlet.Filter#destroy()
	 */
	@Override
	public final void destroy() {
		return;
	}

	/**
	 * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
	 */
	@Override
	public final void init(final FilterConfig filterConfig) throws ServletException {
		this.sc = filterConfig.getServletContext();
		this.inherit = Boolean.parseBoolean(filterConfig.getInitParameter("inherit"));
	}

	/**
	 * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,
	 * javax.servlet.ServletResponse, javax.servlet.FilterChain)
	 */
	@Override
	public final void doFilter(final ServletRequest svRequest, final ServletResponse svResponse,
			final FilterChain chain) throws IOException, ServletException {
		if (HttpServletRequest.class.isInstance(svRequest)
				&& HttpServletResponse.class.isInstance(svResponse)) {
			HttpServletRequest request = HttpServletRequest.class.cast(svRequest);
			HttpServletResponse response = HttpServletResponse.class.cast(svResponse);

			if (!FilterUtil.isView(request)) {
				int loc = findTargetPath(this.sc, request);
				if (0 <= loc) {
					String path = FilterUtil.toPlainURI(request.getRequestURI());
					RequestDispatcher rd = request.getRequestDispatcher(
							getInfix(path, request.getContextPath(), loc)
							+ getTargetPath(getPath(path, loc))
							+ getQueryString(request, path, loc));
					if (FilterUtil.isInclude(request)) {
						rd.include(request, response);
					} else {
						rd.forward(request, response);
					}
					return;
				}
			}
		}
		chain.doFilter(svRequest, svResponse);
	}

	/**
	 * ターゲットパス位置取得
	 *
	 * @param context サーブレットコンテキスト
	 * @param request リクエスト
	 * @return ターゲットパス位置
	 */
	protected abstract int findTargetPath(final ServletContext context,
			final HttpServletRequest request);

	/**
	 * ターゲットパス取得
	 *
	 * @param target ターゲット
	 * @return ターゲットパス
	 */
	protected abstract String getTargetPath(final String target);

	/**
	 * 中間パス取得
	 * @param path URI
	 * @param context ContextPath
	 * @param loc 開始位置
	 * @return 中間パス
	 */
	private String getInfix(final String path, final String context, final int loc) {
		return path.substring(context.length(), loc);
	}

	/**
	 * パス文字列取得
	 * @param path URI
	 * @param loc 開始位置
	 * @return パス文字列
	 */
	private String getPath(final String path, final int loc) {
		int end = path.indexOf('/', loc + "/".length());
		if (end < 0) {
			return path.substring(loc, path.length());
		}
		return path.substring(loc, end);
	}

	/**
	 * クエリ文字列取得
	 *
	 * @param request リクエスト
	 * @param path URI
	 * @param loc クエリ開始位置
	 * @return クエリ文字列
	 */
	private String getQueryString(final HttpServletRequest request,
			final String path, final int loc) {

		StringBuilder sb = new StringBuilder();
		if (request.getQueryString() != null && this.inherit) {
			sb.append(request.getQueryString());
		}

		String[] pathes = path.substring(loc + "/".length()).split("/");
		for (int i = 0; i < pathes.length - 1; i++) {
			if (!pathes[i].isEmpty()) {
				if (0 < sb.length()) {
					sb.append("&");
				}
				sb.append(pathes[i]).append("=").append(pathes[i + 1]);
			}
		}

		if (0 < sb.length()) {
			return "?" + sb.toString();
		}

		return "";
	}
}
